Spring Cloud Config


Spring Cloud Config 可以为分布式系统中外部化配置提供服务器端和客户端的支持,通过 Config Service 即配置中心,可以集中管理所有环境中应用程序配置,这其中主要用于集中管理信息的组件,用于提供配置变更、配置推送、历史版本管理以及灰度发布、配置变更审计等主要的功能,以此来降低分布式系统中所管理和配置信息的成本。

其实 Spring Cloud Config 说简单一点就是通过本地(含 Git/远程代码仓库)来获取配置信息,然后服务提供者A、B…… 从配置中心获取服务,在这个过程中有一个 Spring Cloud Bus 即消息总线,用于通知服务提供者需要获取配置。

目前主流的配置中心有 Spring Cloud Config、Apollo、Nacos、Disconf 等项目,其中 Spring Cloud Config 与 Disconf 是这些项目中最早开源的项目。

但这些项目中,Spring Cloud Config 他的功能全面,以及无缝贴合 Spring 体系,因此大受欢迎。

Config Server

localhost


目前主要且流行的方式就是通过 git 仓库来获取多个或多个配置文件,并通过对称/非对称加密来对密文转为明文,这都的易于 Spring Cloud Config 的特性,如果只满足简单的获取配置文件只需要添加 spring-cloud-config-server 依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

之后在其启动类中加入注解 @EnableConfigServer,并通过 application.yml 全局 Spring 配置中设置仓库地址和帐号密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
application:
name: cloud-server
cloud:
config:
server:
git:
uri: https://gitee.com/sif_one/spring-cloud-config
search-paths: config-repositories
username: xxx
password: xxx
encrypt:
enabled: true # 是否进行解密

server:
port: 8210

git

对于 git 仓库中的配置文件,需要注意的是 - 在访问时会被 / 所代替,如你的配置文件名为 config-dev.properties,那么所请求的 URI 就为 http://localhost:8210/config/dev,当然这是 json 格式的,如果想以常用的格式进行返回,可将 URI 改为其配置文件名 http://localhost:8210/config-dev.properties 即可。

1
2
3
4
app.version=dev
message=Spring Cloud Config Demo
password={cipher}32fd20f46a9c00238236ff530349eefb1dbaad99374c62276f266c326fa8e1ec
server.port=8017

Config Server Client


Spring cloud config client 的主要作用就是通过配置中心来获取配置并使用,主要通过 spring-cloud-starter-config 依赖来进行实现,并通过 bootstrap.yml 文件来达到配置文件切换的效果,最后在使用控制器进行输出:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

bootstrap.yml and application.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: config
profile: rsa
uri: http://localhost:8210
label: master

在 bootstrap.yml 文件中,你可以通过 profile 来切换配置文件的数据,也就是 name(config)profile(rsa),这也是我们配置文件 config-rsa 拼合而成。当然你也可以在 application.yml Spring 全局配置文件中来为其添加应用名称,毕竟他才是权重第一的配置文件。

1
2
3
spring:
application:
name: SpringCloudConfigClient

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 获取配置中心的数据
*
* @author kunlun
* @date 2021/7/26
*/
@RestController
public class heyController {

@Value("${app.version}")
private String version;

@Value("${message}")
private String message;

@RequestMapping("/hey")
public String hey() {
String string = "message:" + message + ":" + " version:" + version;
return string;
}
}

配置文件的加密

对称加密

Config Server 除了管理配置文件和版本管理之外,还支持对文件的对称和非对称加密,其中对称加密简单的来将就是采用单个密钥的方式进行加密,也就是说一个密钥可以加密和解密。

而非对称加密就与对称加密不同了,非对称加密算法分为公钥和私钥,如果用公钥对数据进行加密,那么只能通过私钥才可解密。目前有很多文章说需要使用新版本的 JDK 或者通过 JCE 来进行解决,当然这也是解决方法之一,在新版的 Spring 中,需要引入除 spring-cloud-config-server 之外的新依赖,即 spring-cloud-starter-bootstrap 依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

此时 spring-cloud-starter-bootstrap 会提供三个端口,分别用于加密、解密、检查等方式进行对称加密的操作:

Id Name Info Uri Method
1 /encrypt/status 检查加密密钥是否设置成功 http://localhost:8210/encrypt/status GET
2 /encrypt 加密内容 http://localhost:8210/encrypt POST
3 /decrypt 解密内容 http://localhost:8210/decrypt POST

在原有的 Spring Cloud Config 基础上,我们只需要新建一个 bootstrap.properties 文件并写入密钥,这主要的作用就是,你请求这个接口显示的是明文,而没有密钥的客户端发起请求返回的则是密文。

1
encrypt.key=hey

当然你如果有不良癖好喜欢密文的话,也可以通过 encrypt 来停止 Spring cloud config 的直接翻译,这时返回的则是密文信息:

1
2
3
4
5
6
spring:
cloud:
config:
server:
encrypt:
enabled: true # 是否进行解密

非对称加密


至于非对称加密则需要通过 keytools 来新建一个密钥,之后生成一个密钥移动到 resourcs 目录下,并在 bootstrap 全局配置文件 bbootstrap.applicatiyon 下填写相关的配置信息即可。

1
keytool -genkeypair -alias "spring-cloud-config" -keypass "keypass" -keyalg "RSA" -storepass "stropess" -keystore "spring-cloud-config.jks"

其中上述命令最为关键的部分则是 alias(别名)keypass(密钥口令)storepass(密钥库口令) 这三类主要配置,将会在配置文件中进行使用以验证身份,而 -keylag 则主要表示密钥的类型为 RSA 加密算法。

RSA是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1977年一起提出的,当时他们三人都在麻省理工学院工作,RSA 就是他们三人姓氏开头字母拼在一起组成的。

1
2
3
4
5
6
7
8
# 密钥位置
encrypt.key-store.location=classpath:/spring-cloud-config.jks
# 密钥别名
encrypt.key-store.alias=spring-cloud-config
# 密钥库密码
encrypt.key-store.secret=
# 密钥密码
encrypt.key-store.password=

与对称加密一样,可以通过之前的三个端点来进行加密、解密、状态检查等服务:

Id Name Info Uri Method
1 /encrypt/status 检查加密密钥是否设置成功 http://localhost:8210/encrypt/status GET
2 /encrypt 加密内容 http://localhost:8210/encrypt POST
3 /decrypt 解密内容 http://localhost:8210/decrypt POST

我们在 git 仓库重新建立一个配置文件,他主要通过非对称加密作为密文,之后通过我们的密钥来进行解密的这么一个流程:

1
2
3
4
app.version=rsa
message=Spring Cloud Config Demo
password={cipher}AQA15miINQvSMr5kJorYoX2MLH+1XgjBAGrJKPJWzAvaUhxfrd6So0skCEXjiidn/Vgf+aWCcz17pugBoRqEeDU6XLyu1tS3dAyNE/z7vbyEcp+DsJOEqHiHnr3VqzrhQTJheJOibkskvd8kJt6WP4F9Tl8qyen6mOz+4/Ce4x9iCzPoWTO2Yc+jXJFAjOSB02/kh1wnqAdyc0s7cDYLjjR+FBSMFjFJXV0Qax28hZNjjdlUb/Hy+8mPaSAQbipc9FIKB+dQaSAABYCviscHz7XkjpfmQN3qOAYiMum851OP1OOn8KHGpUu1u7D8ET+404iAHjNPTKh/As8T9A92Av73YE6b8Dj3m9wZf4hCUyEMFUpo0kffw9ThOAzVgR1RnNY=
server.port=8017

Spring Cloud Bus

Spring Cloud Bus 将会用于自动刷新配置,并且可以管理和传播分布式项目中的消息,利用消息中间件广播的机制传播可以实现将消息连接整个集群,他目前支持 Kafka 和 RabbitMQ,其中目前主要流行的就是这两个项目。

RabbitMQ


RabbitMQ 是一个开源的消息代理项目,即面向消息的中间件,采用 Erlang 语言来进行编写,因此我们除了安装 RabbitMQ 之外还需要安装 Erlang 。

对于使用 debian 系的 Linux 用户,可以通过使用 apt-get install erlang 直接进行安装 erl,之后同样的安装 RabbitMQ Server 并运行:

1
2
apt-get install rabbitmq-server
systemctl status rabbitmq-server

运行之后就开始进行配置 RabbitMQ 了,这里面主要的就是配置帐号密码以及权限和安装可视化插件:

Id Name Info
1 rabbitmq-plugins enable rabbitmq_management 安装可视化插件
2 rabbitmqctl add_user root toor 添加 root 帐号密码为 toor
3 set_user_tags root administrator 将 root 帐号设置为管理
4 set_permissions -p / root ".*" ".*" ".*" 为 root 帐号添加写入权限
5 rabbitmqctl list_permissions 查看当前的当前的权限列表

值得注意的是,当 RabbimtMQ 配置完成后,他会提供三个非常重要的端口,分别为 5672\25672\15672 在本文中主要使用 5672 端口来进行 AMQP 连接接口,而 15672 用于 HTTP 访问接口,也就是监控中心,之后你可以将这个接口添加到 Config Server & Client 配置文件中:

1
2
3
4
5
6
7
8
spring:
application:
name: SpringCloudConfigClient
rabbitmq:
username: root
password: toor
host: xxx
port: 5672

Config Server


对于 Config Server ,我们可以理解在原有的基础上,增加了 Spring Cloud Config Bus 来实现配置文件更新和广播的作用,简单来讲就是默认情况下 Config Client 是无法实现自动更新的,只可以通过 Spring Config Bus 来进行实现发送 POST 来进行刷新客户端配置文件的应用,因此我们需要添加如下依赖(在原有的非对称加密的基础上配置 Config Server):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
spring:
application:
name: cloud-server
cloud:
config:
server:
git:
uri: https://gitee.com/sif_one/spring-cloud-config
search-paths: config-repositories
username: xxx
password: xxx
encrypt:
enabled: true # 是否进行解密
rabbitmq:
username: root
password: toor
host: xxx
port: 5672
server:
port: 8210

management:
endpoints:
web:
exposure:
include: "bus-refresh"

之后打包上传到服务器之中,运行后即可通过使用 http://111.67.201.159:8210/actuator/busrefresh 接口来进行刷新客户端的配置,这是新版本中所提供的接口,当然你如果使用的是其他版本也可以通过访问 /actuator 端口来查看目前所支持的 Spring-Cloud-Bus 接口。

Config Client

Config Client 的主要作用就是获取到 Config Server 从 Git 仓库中的配置文件并运用,但是在不使用 Spring-Cloud-Config-Bus 的情况下,即使你将最新的配置文件上传了 Config Config 也不会进行更新,因此需要通过 Spring Cloud Config Bus 来进行实现,在原有的 Config CLient 基础上进行添加即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-config-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-amqp</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

@RefreshScope

.@RefreshScope 依赖主要的作用就是当 git 仓库发生改变或更新后,我们通过 Config server 所提供的 actuator/busrefresh POST 请求来刷新 Config Client 的配置文件,因此这个依赖主要的作用就是刷新的范围,我们在原有的 Config Server Client 基础上添加即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* 获取配置中心的数据
*
* @author kunlun
* @date 2021/7/26
*/
@RestController
@RefreshScope
public class heyController {

@Value("${app.version}")
private String version;

@Value("${message}")
private String message;

@RequestMapping("/hey")
public String hey() {
String string = "message:" + message + ":" + " version:" + version;
return string;
}
}

当然你也可以在启动类中进行添加

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
application:
name: SpringCloudConfigClient
rabbitmq:
username: root
password: toor
host: xxx
port: 5672

management:
endpoints:
web:
exposure:
include: "bus-refresh"

配置文件中的 management 为开启此接口,默认的情况下是关闭的,需要通过配置文件中进行配置,之后即可访问,虽然在最新版本中不知道有没有什么作用,但是为了保险期间还是添加为好。

新版本中 Spring Cloud Config Bus 所提供的 POST 更新接口为 actuator/busrefresh

bootstrap.yml

1
2
3
4
5
6
7
spring:
cloud:
config:
name: config
profile: rsa
uri: http://xxx:8210
label: master

bootstrap 配置文件主要做的就是通过 spring.cloud.config 来链接配置中心,并通过配置中心所获取的配置文件来进行运用,最后配置完成后我们可以尝试修改配置文件信息,你可以全部接口都访问一遍看看最初的效果(除了 actuator/busrefresh),之后通过 actuator/busrefresh 接口以 POST 方式进行刷新,最后访问 Spring Cloud Config Client 所提供的 /hey 接口来查看是否刷新成功。

WebHooks

WebHooks 主要的作用就是当推送到 git 仓库的时候 WebHooks 服务会将 POST 请求到对应的 uri 中,从而实现我们手动请求 POST 来刷新服务配置文件的效果。但在新版本中却请求失败,考虑其他不可抗拒的因素建议国内读者选择 Gitte,但 WebHooks 依然请求返回 400 如有读者解决可在下放进行评论。

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布